﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.InteropServices;
using System.Collections;
using System.Diagnostics;

namespace Liquifier
{
    public partial class Liquifer
    {
        #region Saving and Loading Data

        protected void CollectMemberValues(object o)
        {
            foreach (Member m in LocalMembers.MembersList)
            {
                MemberContents.Add(new MemberValue(m, m.GetValue(o)));
            }
        }

        private Dictionary<string, List<Member>> memberCache = new Dictionary<string, List<Member>>();

        protected IEnumerable<Member> FindMembers(string name)
        {
            var members = LocalMembers.FindMemberValue(name);
            if (members == null)
            {
                Debug.WriteLine(string.Format("Liquifier: Warning: Encountered serialised member '{0}' but could not find member to deserialise to.", name));
                members = new List<Member>();
            }
            return members;
        }

        protected void RepatriateMemberValues(object destination)
        {
            foreach (MemberValue d in MemberContents)
            {
                if ((d.Flags & SerialisedMemberFlags.DoNotProcessOnSolidify) > 0)
                    continue;

                foreach (Member m in FindMembers(d.MemberName))
                {
                    if (m.LiquifyOptions.Contains(SerialisationOptions.SerializeOnly))
                        continue;

                    if (d.Value is IList)
                    {
                        if (m.LiquifyOptions.Contains(SerialisationOptions.UpdateExistingIList))
                        {
                            IList existingList = m.GetValue(destination) as IList;
                            if (existingList != null)
                            {
                                IList sourceList = d.Value as IList;
                                foreach(object o in sourceList)
                                {
                                    existingList.Add(o);
                                }
                                continue;
                            }
                        }
                    }

                    m.SetValue(d.Value, destination);
                }
            }
        }

        #endregion

        #region Writing the type and recreating the object


        const int MagicNumber = 0xACDC;

        protected void Serialise(SerialisationWriter writer)
        {
            writer.Write("Liquifier Smart Serialisation, Hi :)");
            writer.Write((Int32)MagicNumber);

            writer.Write(TargetType.AssemblyQualifiedName);

            writer.Write((Int32)MemberContents.Count);

            foreach (var mc in MemberContents)
            {
                writer.Write(mc.MemberName);
                writer.Write((Byte)mc.Flags);
                writer.WriteObject(mc.Value, this.LiquifyContext);
            }
        }

        protected void DeserialiseContent(SerialisationReader reader)
        {
            Int32 memberCount = reader.ReadInt32();

            for (int i = 0; i < memberCount; i++)
            {
                string memberName = reader.ReadString();
                SerialisedMemberFlags flags = (SerialisedMemberFlags)reader.ReadByte();
                object memberValue = reader.ReadObject(this.SolidifyContext, -1);

                MemberContents.Add(new MemberValue(memberName, memberValue, flags));
            }
        }

        protected void DeserialiseType(SerialisationReader reader)
        {
            string header = reader.ReadString();
            Int32 magicnumber = reader.ReadInt32();

            if (magicnumber != MagicNumber)
            {
                throw new Exception("Magic number doesn't match - weve lost sync!");
            }

            string assemblyqualifiedtypename = reader.ReadString();
            this.TargetType = TypeData.GetTypeData(assemblyqualifiedtypename);

            if (this.TargetType == null)
            {
                throw new Exception("Unable to find type.");
            }
        }

        #endregion
    }
}
